/**
 * Copyright 2009 the original author or authors.
 *
 * Licensed under the GPL License, Version 3.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.gnu.org/licenses/lgpl-3.0.txt
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.guardianapis.api.serialization;

import java.io.InputStream;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.guardianapis.api.client.GuardianConstants;
import com.guardianapis.api.domain.jaxb.Content;
import com.guardianapis.api.domain.jaxb.Results;
import com.guardianapis.api.domain.jaxb.Search;
import com.guardianapis.api.domain.jaxb.Tag;
import com.guardianapis.api.domain.jaxb.TagList;
import com.guardianapis.api.domain.jaxb.Tags;
import com.guardianapis.api.domain.jaxb.TypeSpecific;
import com.guardianapis.api.util.StreamLogger;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.converters.extended.ISO8601GregorianCalendarConverter;

public class XStreamContentSerializer implements GuardianContentSerializer {

	/*******************************************************************************
	 * Constants
	 *******************************************************************************/

	private Log log = LogFactory.getLog(XStreamContentSerializer.class);

	/*******************************************************************************
	 * Properties
	 *******************************************************************************/

	private XStream xStream;

	/*******************************************************************************
	 * Constructors
	 *******************************************************************************/

	/**
	 * 
	 */
	public XStreamContentSerializer() {
		
		// Initializing XStream and generic configuration
		
		this.xStream = new XStream();
		this.xStream.registerConverter(new ISO8601GregorianCalendarConverter());
		this.xStream.aliasSystemAttribute("x-class", "class");
		this.xStream.aliasSystemAttribute("x-id", "id");
		
		//this.xStream.registerConverter(new XMLGregorianCalendarConverter(Locale.getDefault()));
		
		// Configuring XStream for the Results class
		
		this.xStream.alias("results", Results.class);
		this.xStream.addImplicitCollection(Results.class, "content", Content.class);
		this.xStream.aliasAttribute(Tag.class, "apiUrl", "api-url");
		this.xStream.useAttributeFor(Tag.class, "count");
	    
		// Configuring XStream for the Content class
		
		this.xStream.alias("content", Content.class);
		this.xStream.useAttributeFor(Content.class, "id");
		this.xStream.useAttributeFor(Content.class, "type");
		this.xStream.aliasAttribute(Content.class, "webUrl", "web-url");
		this.xStream.aliasAttribute(Content.class, "apiUrl", "api-url");
		this.xStream.aliasField("section-name", Content.class, "sectionName");
		this.xStream.aliasField("trail-text", Content.class, "trailText");
		this.xStream.aliasField("trail-image", Content.class, "trailImage");
		this.xStream.aliasField("link-text", Content.class, "linkText");
		this.xStream.aliasField("publication-date", Content.class, "publicationDate");
		this.xStream.aliasField("tagged-with", Content.class, "taggedWith");
		this.xStream.aliasField("type-specific", Content.class, "typeSpecific");
		
		// Configuring XStream for the Search class
		
		this.xStream.alias("search", Search.class);
		this.xStream.useAttributeFor(Search.class, "count");
		this.xStream.aliasAttribute(Search.class, "startIndex", "start-index");

		// Configuring XStream for the Tag class
		
		this.xStream.alias("tag", Tag.class);
		this.xStream.aliasAttribute(Tag.class, "apiUrl", "api-url");
		this.xStream.useAttributeFor(Tag.class, "count");
		this.xStream.useAttributeFor(Tag.class, "filter");
		this.xStream.aliasAttribute(Tag.class, "filterUrl", "filter-url");
		this.xStream.useAttributeFor(Tag.class, "name");
		this.xStream.useAttributeFor(Tag.class, "type");
		this.xStream.aliasAttribute(Tag.class, "webUrl", "web-url");

		// Configuring XStream for the TagList class
		
		this.xStream.alias("tag-list", TagList.class);
		this.xStream.addImplicitCollection(TagList.class, "tag", Tag.class);

		// Configuring XStream for the Tags class
		
		this.xStream.alias("tags", Tags.class);
		this.xStream.useAttributeFor(Tags.class, "count");
		this.xStream.aliasAttribute(Tags.class, "startIndex", "start-index");
		this.xStream.addImplicitCollection(Tags.class, "tag", Tag.class);
		
		// Configuring XStream for the TypeSpecific class

		this.xStream.alias("type-specific", TypeSpecific.class);
		this.xStream.aliasField("duration-minutes", TypeSpecific.class, "durationMinutes");
		this.xStream.aliasField("duration-seconds", TypeSpecific.class, "durationSeconds");
		this.xStream.aliasField("show-notes", TypeSpecific.class, "showNotes");
		this.xStream.aliasField("alt-text", TypeSpecific.class, "altText");
		this.xStream.aliasField("picture-count", TypeSpecific.class, "pictureCount");

		log.info("Created XStream deserializer");
	}
	
	/*******************************************************************************
	 * Implementing GuardianContentSerializer
	 *******************************************************************************/

	public Search deSerializeSearch(InputStream pXmlSearchInputStream) {
		if (log.isDebugEnabled()) {
			log.debug("Search stream: " + StreamLogger.getStreamCharacters(pXmlSearchInputStream, GuardianConstants.NUM_CHARACTERS_TO_LOG_FROM_STREAM));
		}
		Search deserializedSearch = (Search) this.xStream.fromXML(pXmlSearchInputStream);
		return deserializedSearch;
	}
	
	public Content deSerializeContent(InputStream pXmlContentStream) {
		if (log.isDebugEnabled()) {
			log.debug("Content stream: " + StreamLogger.getStreamCharacters(pXmlContentStream, GuardianConstants.NUM_CHARACTERS_TO_LOG_FROM_STREAM));
		}
		Content deserializedContent = (Content) this.xStream.fromXML(pXmlContentStream);
		return deserializedContent;
	}
	
	public Tags deSerializeTags(InputStream pXmlTagsStream) {
		if (log.isDebugEnabled()) {
			log.debug("Tags stream: " + StreamLogger.getStreamCharacters(pXmlTagsStream, GuardianConstants.NUM_CHARACTERS_TO_LOG_FROM_STREAM));
		}
		Tags deserializedAllSubjects = (Tags) this.xStream.fromXML(pXmlTagsStream);
		return deserializedAllSubjects;
	}

	/*******************************************************************************
	 * Protected Methods
	 *******************************************************************************/

	/**
	 * Return the underlying XStream serializer for use in unit testing
	 */
	protected XStream getXStream() {
		return this.xStream;
	}
}
